\chapter{Лекция 2. Процесс}

\section{Процессы и программы}

Процесс и программа не одно и то же, очевидность конечно, но все же повторимся. Процесс в ОС является единицей выделения и контроля ресурсов, т. е. понятие процесса
в первую очередь связано с понятием ресурса, особенно двух видов: cpu и память. Каждый процесс связан с потоком исполнения, то есть с последовательностью исполнимых
комманд процессора.

\section{Процессы и потоки в Linux}

В первую очередь напомним про два известных системных вызова: fork и exec*. Первый осуществляет полное копирование текущего процесса, т. е. порождает процесс
почти полностью идентичный исходному, второй осуществляет выделение памяти и загрузку исполнимого файла с параметрами. Впринципе все это известно и довольно
стандартно для Unix систем.

\subsection{Дескриптор процесса в Linux}

Дескриптор процесса в Linux есть struct task\_struct, описанная в файле include/linux/sched.h. Рассмотрим самые интересные поля:
\begin{lstlisting}
struct task_struct {
	volatile long state; \\(1)
	void *stack; \\(2)
	...
	unsigned int flags; \\(3)
	...
	
\*(4)*\
#ifdef CONFIG_SMP
	struct llist_node wake_entry;
	int on_cpu; \\(5)
#endif
	...
};
\end{lstlisting}

(1) - состояние процесса, их где-то около 5 штук;

(2) - очевидно, указатель на стек;

(3) - флаги связанные с процессом, обсудим позже;

(4) - проверка на включение SMP в конфиге ядра;

(5) - номер cpu, на котором исполняется процесс, в SMP системах приходится на это опираться;

Более интересная структура для планировщика struct thread\_info, которая находится в каталоге системно-зависимых файлов, например,
arch/x86/include/asm/thread\_info.h, она гораздо меньше чем task\_struct, но используется гораздо активнее, она содержит всю информацию, которая необходима
планировщику для переключения контекста:

\begin{lstlisting}
struct threaed_info {
	struct task_struct	*task;
	struct exec_domain	*exec_domain; //(1)
	__u32			flags;		/* low level flags */
	__u32			status;		/* thread synchronous flags */
	__u32			cpu;		/* current CPU */
	int			preempt_count;	//(2)
						   <0 => BUG */
	mm_segment_t		addr_limit;
	struct restart_block    restart_block;
	void __user		*sysenter_return;
#ifdef CONFIG_X86_32
	unsigned long           previous_esp;   /* ESP of the previous stack in
						   case of nested (IRQ) stacks
						*/
	__u8			supervisor_stack[0];
#endif
	int			uaccess_err;
};
\end{lstlisting}

(1) - базовая система не различает при планировании процессы, т. е. все они в некотором смысле равнозначны, это поле позволяет некоторым механизмам это исправить

(2) - счетчик входа в процесс, например, если мы пытаемся войти в прерывание несколько раз (т. е. если мы вошли в прерывание еще до того как мы обработали
предыдущее), то этот счетчик увеличивается.

Структура thread\_info всегда лежит в начале стека процесса и к ней легко обратиться.

Количество процессов в системе по-умолчанию ограничено числом 32768, это число можно поменять как в конигах ядра так и на лету, за сим отправляем вас к файлу
/proc/sys/kernel/pid\_max файловой системы proc, который позволяет узнать и изменить это число на лету.

Чтобы обратиться к контексту процесса в коде ядра используется макрос current, который возвращает указатель на struct task\_struct. Вообще в ядре вполне нормально
для обеспечения лучшей переносимость использовать всякие сложные системы макросов, так как получение контекста процесса архитектурно зависимая вещь и реализуется
на разных системах по-разному над всеми этими реализациями существует обертка-макрос, так что в коде ядра используте эти макросы!

Контекст процесса не всегда определен, например, код ядра может передаваться в контексте прерывания, которое никак не привязано к какому-либо процессу, и в этом
случае current вернет NULL.

Аналогично макросу current есть еще и макрос current\_thread\_info, который соответственно возвращает указатель на struct thread\_info.

\subsection{Состояния процессов в Linux}

В Linux возможны следующие состояния процессов:

\begin{itemize}
\item TASK\_RUNNING - означает, что процесс либо готов к исполнению и ждет очереди, либо исполняется, различие лишь в очереди, в которой находится процесс

\item TASK\_INTERAPTABLE - процесс заблокирован на ожидание, может получить управление, если к нему придет сигнал, т. е. не обязательно по выполнению условия
ожидания

\item TASK\_UNINTERAPTABLE - аналогично предыдущему, только не может получать сигналы (kill -9 его не колышит и это забавно), примером такого процесса может быть
pdflush или ksoftirq

\item TASK\_ZOMBIE - процесс уже завершен, но его родитель, еще не получил код возврата дочернего процесса т. е. родитель еще не отследил завершение своего
потомка, к примеру, родитель еще не вызвал wait для него.

\item TASK\_STOPPED - если нажать в консоли ctrl + z, и процесс останавливается и уходит в background и там замирает, вот этот замерший процесс находится в
состоянии TASK\_STOPPED. Иногда процесс переходит в это состояние, если выполняет некоторые операции ввода или вывода, на которых он блокируется до завершения.
\end{itemize}

Для задания состояния процесса используются два макроса: set\_task\_state и set\_current\_state.

\subsection{Отношения между процессами}

Понятно, что все процессы в Linux имеют иерархическую структуру (отношения родитель-потомок), но какие еще отношения возможны между процессами? Кроме того
существует связь с сиблингами (братьями и сестрами), в ядре все это осуществляется посредством организации всех структур процессов в кучу разных списков, и проход
по этим спискам отражает различные отношения между процессами, ходить по этим спискам опять же можно кучей всяких макросов.

\subsection{Порждение процессов в ядре}

Порождение процессов в ядре происходит вызовом clone(), который обращается к do\_fork(), задача которого проверить все лимиты, выделить память и создать структуры
данных, затем на короткое время процесс переводится в состояние TASK\_UNINTERAPTABLE, затем происходит вызов copy\_process, единственная полезная вещь, которую он
делает, это копирует флаги процессе родителя. Далее процесс переходит в рабочее состояние и начинает выполняться, ну и самое интересно, все это происходит прямо
в родительском кванте времени до перепланировки.

\subsection{Потоки в ядре Linux}

Потоки в Linux с точки зрения ядра не отличаются от процессов, просто различные указатели на ресурсы внутри структуры процесса соответствующего потоку указывают
для всех таких потоков в одно место, с точки зрения планировщика потоки и процессы не различаются. Разница лишь в вызове clone, в параметрах которого указывается
какие ресурсы копируются при клонировании процесса, а какие будут расшарены.

Кроме того, существуют так называемые потоки ядра, которые отличаются от пользовательский потоков отсутствием адресного процесса, так как они находятся в ядре и им
не нужно иметь user space пространсва, создавать такие потоки в ядре можно с помщью вызова kthread* (не помню точно как называется, но по kthread его легко найти).